//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

namespace Microsoft.Research.ScientificWorkflow.UIDesigner.Monitor
{
    using System;
    using Microsoft.Research.ScientificWorkflow.TridentModel;
    using Microsoft.Research.ScientificWorkflow.UIDesigner.Commands;
    using Microsoft.Research.ScientificWorkflow.UIDesigner.WpfComposite;
    using Microsoft.Practices.Unity;
    using Microsoft.Research.eResearch.Blackboard.Common;
    using Microsoft.Research.eResearch.Subscriber.Interfaces;

    public class ExecutionVisualizerPresentationModel
    {
        private IUnityContainer container;

        public ExecutionVisualizerPresentationModel(IUnityContainer container, JobCommandProxy jobCommandProxy)
        {
            this.container = container;
            jobCommandProxy.ExecuteCommand.RegisterCommand(new DelegateCommand<object>(p => { this.LoadWorkflowModel(); }));
        }

        /// <summary>
        /// Initializes this instance.
        /// </summary>
        public void Initialize()
        {
            try
            {
                container.Resolve<IMonitorService>().WorkflowInstanceCreated += delegate {  /* this.LoadWorkflowModel(); */ };
                container.Resolve<IMonitorService>().ProcessingStatus += OnProcessingStatusRecieved;
            }
            catch (ResolutionFailedException)
            {
                // Ignore. Still need to show the visualizer even if the blackboard service is off.
            }
        }

        private void LoadWorkflowModel()
        {
            ComposerControl composerControl = container.Resolve<ComposerControl>("Monitor");
            UIHelper uiHelper = container.Resolve<UIHelper>("UiHelper");
            uiHelper.MonitorWorkflowModel = TridentWorkflowModel.CreateClone(uiHelper.ComposerWorkflowModel);
            if (uiHelper.MonitorWorkflowModel != null)
            {
                composerControl.ResetTransforms();
                composerControl.InitializeSubsectionHandlers(uiHelper.MonitorWorkflowModel);
                uiHelper.MonitorWorkflowModel.CollapseModels();
                composerControl.LoadWorkflow(uiHelper.MonitorWorkflowModel, false);
            }
        }

        private void OnProcessingStatusRecieved(object sender, BlackboardMessageReceivedEventArgs e)
        {
            UIHelper uiHelper = container.Resolve<UIHelper>("UiHelper");
            if (IsActivityEvent(e.IncomingMessage))
            {
                string uniqueName = e.IncomingMessage["trident:ActivityName"];
                string state = e.IncomingMessage["trident:ActivityState"];

                BaseModel model = (uiHelper.MonitorWorkflowModel != null) ? uiHelper.MonitorWorkflowModel.GetModelByUniqueId(uniqueName) : null;

                if (model != null && (model.IsSimpleActivityModel || model.IsCompositeActivityModel || model.IsSimpleBlackBoxModel || model.IsCompositeBlackBoxModel))
                {
                    switch (state)
                    {
                        case "Executing":
                            ExecutionVisualizerPresentationModel.AssignExecutionStatus(model, TridentActivityExecutionStatus.Executing);
                            break;
                        case "Closed":
                            // Executing, Faulting, Closed is the order of events raised by WF runtime    
                            // If the previous state of the activity is Executing, then change the state to Closed.
                            // Closed event after Faulting will change the actvity execution animation UI to wrong 
                            TridentActivityExecutionStatus execStatus = TridentActivityExecutionStatus.Executing;
                            if (model.IsSimpleActivityModel)
                            {
                                execStatus = (model as SimpleActivityModel).CurrentTridentActivityExecutionStatus;
                            }
                            else if (model.IsCompositeActivityModel)
                            {
                                execStatus = (model as CompositeActivityModel).CurrentTridentActivityExecutionStatus;
                            }
                            else
                            {
                                break;
                            }

                            if (execStatus == TridentActivityExecutionStatus.Executing)
                            {
                                ExecutionVisualizerPresentationModel.AssignExecutionStatus(model, TridentActivityExecutionStatus.Closed);
                            }

                            break;

                        case "Faulting":
                            ExecutionVisualizerPresentationModel.AssignExecutionStatus(model, TridentActivityExecutionStatus.Faulting);
                            break;
                    }
                }
            }
            else if (IsTerminateMessage(e.IncomingMessage))
            {
                uiHelper.MonitorWorkflowModel.ChangeAllExecutingStatusToFaulting();
            }
        }

        private static void AssignExecutionStatus(BaseModel model, TridentActivityExecutionStatus executionStatus)
        {
            if (model.IsSimpleActivityModel)
            {
                ExecutionVisualizerPresentationModel.AssignExecutionStatus(model as SimpleActivityModel, executionStatus);
            }
            else if (model.IsCompositeActivityModel)
            {
                ExecutionVisualizerPresentationModel.AssignExecutionStatus(model as CompositeActivityModel, executionStatus);
            }
            else
            {
                return;
            }
        }

        private static void AssignExecutionStatus(SimpleActivityModel simpleModel, TridentActivityExecutionStatus executionStatus)
        {
            simpleModel.CurrentTridentActivityExecutionStatus = executionStatus;
        }

        private static void AssignExecutionStatus(CompositeActivityModel compositeModel, TridentActivityExecutionStatus executionStatus)
        {
            compositeModel.CurrentTridentActivityExecutionStatus = executionStatus;
        }

        private static bool IsActivityEvent(BlackboardMessage message)
        {
            return message.ContainsKey("trident:ActivityState");
        }

        private static bool IsTerminateMessage(BlackboardMessage message)
        {
            return message.ContainsKey("trident:WorkflowState") && (message["trident:WorkflowState"] == "Terminated");
        }
    }
}
